#include "Game.h"

CGame::CGame(HINSTANCE hInstance, char* Name, int Mode, int IsFullScreen, int FrameRate)
{
	G_DirectX = NULL;
	G_Device = NULL;
	G_BackBuffer = NULL;

	G_DirectInput = NULL;
	G_KeyBoard = NULL;

	G_Mode = Mode;
	_SetScreenDimension(Mode);
	G_GameName = Name;
	G_IsFullScreen = IsFullScreen;
	G_FrameRate = FrameRate;

	G_hInstance = hInstance;
}

void CGame::_SetScreenDimension(int Mode)
{
	switch (Mode)
	{
	case GL_RESOLUTION_640_480_24:
		G_ScreenWidth = 640; 
		G_ScreenHeight = 480;
		G_GameDepth = 24;
		G_BackBufferFormat = D3DFMT_X8R8G8B8;
		break;

	case GL_RESOLUTION_800_600_24:
		G_ScreenWidth = 800; 
		G_ScreenHeight = 600;
		G_GameDepth = 24;
		G_BackBufferFormat = D3DFMT_X8R8G8B8;
		break;

	case GL_RESOLUTION_1024_768_24:
		G_ScreenWidth = 1024; 
		G_ScreenHeight = 768;
		G_GameDepth = 24;
		G_BackBufferFormat = D3DFMT_X8R8G8B8;
		break;

	default: 
		break;
	}
}

void CGame::InitGame()
{
}

void CGame::_InitWindow()
{
	WNDCLASSEX wc;
	wc.cbSize = sizeof(WNDCLASSEX);

	wc.style = CS_HREDRAW | CS_VREDRAW;
	wc.hInstance = G_hInstance;

	wc.lpfnWndProc = (WNDPROC)CGame::_WinProc;
	wc.cbClsExtra = 0;
	wc.cbWndExtra = 0;
	wc.hIcon = NULL;
	wc.hCursor = LoadCursor(NULL, IDC_ARROW);
	wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wc.lpszMenuName = NULL;
	wc.lpszClassName = G_GameName;
	wc.hIconSm = NULL;
	
	RegisterClassEx(&wc);

	DWORD style; 
	if (G_IsFullScreen)
		style = WS_EX_TOPMOST | WS_VISIBLE | WS_POPUP;
	else 
		style = WS_OVERLAPPEDWINDOW;

	G_hWnd = 
		CreateWindow(
			G_GameName,
			G_GameName,
			style, 
			CW_USEDEFAULT,
			CW_USEDEFAULT, 
			G_ScreenWidth,
			G_ScreenHeight,
			NULL,
			NULL,
			G_hInstance,
			NULL);
	
	if (!G_hWnd) 
	{ 
		//trace(L"[ERROR] Failed to created window!");
		DWORD ErrCode = GetLastError();
	}

	ShowWindow(G_hWnd,SW_SHOWNORMAL);
	UpdateWindow(G_hWnd);
}

void CGame::_InitDirectX()
{
	G_DirectX = Direct3DCreate9(D3D_SDK_VERSION);
	D3DPRESENT_PARAMETERS d3dpp; 

	ZeroMemory( &d3dpp, sizeof(d3dpp) );

	if(G_IsFullScreen) {
		d3dpp.Windowed   = FALSE;
	} else {
		d3dpp.Windowed   = TRUE;
	}
	
	d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
	d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;
	d3dpp.BackBufferCount = 1;
	d3dpp.BackBufferHeight = G_ScreenHeight;
	d3dpp.BackBufferWidth = G_ScreenWidth;

	G_DirectX->CreateDevice(
		D3DADAPTER_DEFAULT,
		D3DDEVTYPE_HAL,
		G_hWnd,
		D3DCREATE_SOFTWARE_VERTEXPROCESSING,
		&d3dpp,
		&G_Device);

	if (G_Device==NULL) 
	{
		return;
	}

	//backbuffer
	G_Device->GetBackBuffer(0,0,D3DBACKBUFFER_TYPE_MONO,&G_BackBuffer);

	//sprite handler
	D3DXCreateSprite(G_Device, &G_SpriteHandler);
}

void CGame::_InitKeyboard()
{
	HRESULT 
		hr = DirectInput8Create
			( 
				G_hInstance, 
				DIRECTINPUT_VERSION, 
				IID_IDirectInput8, (VOID**)&G_DirectInput, NULL 
			);

	// TO-DO: put in exception handling
	if (hr!=DI_OK) return;
	
	//trace(L"DirectInput has been created");

	hr = G_DirectInput->CreateDevice(GUID_SysKeyboard, &G_KeyBoard, NULL); 
	
	// TO-DO: put in exception handling
	if (hr!=DI_OK) return;

	//trace(L"DirectInput keyboard has been created");

	// Set the data format to "keyboard format" - a predefined data format 
	//
	// A data format specifies which controls on a device we
	// are interested in, and how they should be reported.
	//
	// This tells DirectInput that we will be passing an array
	// of 256 bytes to IDirectInputDevice::GetDeviceState.

	hr = G_KeyBoard->SetDataFormat(&c_dfDIKeyboard);

	//trace(L"SetDataFormat for keyboard successfully");

	hr = G_KeyBoard->SetCooperativeLevel(G_hWnd, DISCL_FOREGROUND | DISCL_NONEXCLUSIVE); 

	//trace(L"SetCooperativeLevel for keyboard successfully");

	// IMPORTANT STEP TO USE BUFFERED DEVICE DATA!
	//
	// DirectInput uses unbuffered I/O (buffer size = 0) by default.
	// If you want to read buffered data, you need to set a nonzero
	// buffer size.
	//
	// Set the buffer size to DINPUT_BUFFERSIZE (defined above) elements.
	//
	// The buffer size is a DWORD property associated with the device.
	DIPROPDWORD dipdw;

	dipdw.diph.dwSize       = sizeof(DIPROPDWORD);
	dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
	dipdw.diph.dwObj        = 0;
	dipdw.diph.dwHow        = DIPH_DEVICE;
	dipdw.dwData            = GL_KEY_BUFFER_SIZE; // Arbitary buffer size

	//trace(L"SetProperty for keyboard successfully");

	hr = G_KeyBoard->SetProperty( DIPROP_BUFFERSIZE, &dipdw.diph );
	if (hr!=DI_OK) return;

	hr = G_KeyBoard->Acquire(); 
	if (hr!=DI_OK) return;

	//trace(L"Keyboard has been acquired successfully");
}

void CGame::Init()
{
	_InitWindow();
	_InitDirectX();
	_InitKeyboard();
	InitGame();
}

void CGame::_ProcessKeyBoard()
{
		// Collect all key states first

		G_KeyBoard->GetDeviceState( sizeof(_KeyStates), _KeyStates);
		//G_KeyBoard->Acquire();
		//if (IsKeyDown(DIK_ESCAPE)) 
		//{
		//	//trace(L"Escape key pressed!");
		//	PostMessage(_hWnd,WM_QUIT,0,0);
		//}

		// Collect all buffered events
		DWORD dwElements = GL_KEY_BUFFER_SIZE;
		HRESULT hr = G_KeyBoard->GetDeviceData( sizeof(DIDEVICEOBJECTDATA), _KeyEvents, &dwElements, 0 );

		// Scan through all data, check if the key is pressed or released
		for( DWORD i = 0; i < dwElements; i++ ) 
		{
			int KeyCode = _KeyEvents[i].dwOfs;
			int KeyState = _KeyEvents[i].dwData;
			if ( (KeyState & 0x80) > 0)
				OnKeyDown(KeyCode);
			else 
				OnKeyUp(KeyCode);
		}
}



// Main game message loop
void CGame::Run()
{
	MSG msg;
	int done = 0;
	DWORD frame_start = GetTickCount();;
	
	DWORD tick_per_frame = 1000 / G_FrameRate;
	
	//trace(L">>> Main game loop has been started");

	while (!done) 
	{
		if (PeekMessage(&msg,NULL,0,0,PM_REMOVE))
		{
			if (msg.message==WM_QUIT) done=1;

			TranslateMessage(&msg);
			DispatchMessage(&msg);			
		}

		DWORD now = GetTickCount();
		G_DeltaTime = now - frame_start; 
		if (G_DeltaTime >= tick_per_frame)
		{
			frame_start = now;
			_Update();
			_RenderFrame();
		}

		_ProcessKeyBoard();

		ProcessInput();
	}

	//trace(L"Main game loop has ended");
}

void CGame::_Update()
{
	Update(G_DeltaTime);
}
 
void CGame::Update(int Delta)
{
}

void CGame::_RenderFrame()
{
	if (G_Device->BeginScene()) 
	{
		RenderFrame();
		G_Device->EndScene();
	}
	G_Device->Present(NULL,NULL,NULL,NULL);
}

void CGame::RenderFrame() 
{
	G_Device->ColorFill(G_BackBuffer,NULL,D3DCOLOR_XRGB(0,0,0));
}

void CGame::ProcessInput() { }

CGame::~CGame()
{
	if (G_Device!=NULL) G_Device->Release();
	if (G_DirectX!=NULL) G_DirectX->Release();

	if( G_KeyBoard )  
	{
		G_KeyBoard->Unacquire();
		G_KeyBoard->Release();
	}

	if (G_DirectInput) G_DirectInput->Release();
}

int CGame::IsKeyDown(int KeyCode)
{
	return (_KeyStates[KeyCode] & 0x80) > 0;
}

void CGame::OnKeyUp(int KeyCode) { }
void CGame::OnKeyDown(int KeyCode) { }


LRESULT CALLBACK CGame::_WinProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch (message) 
	{
		case WM_DESTROY: 
			PostQuitMessage(0);
			break;
			;
		default: 
			return DefWindowProc(hWnd, message, wParam,lParam);
	}

	return 0;
}
